import { DefaultOptions, Options, TrackerConfig } from "../types/index";
import { createHistoryEvent } from "../utils/pv";

const MouseEventList = ['click', 'dblclick', 'contextmenu', 'mousedown', 'mouseup', 'mouseenter', 'mouseout', 'mouseover']

export default class Tracker {
  public data: Options
  constructor(options: Options) {
    this.data = Object.assign(this.initOptions(), options)
    this.installTracker()
  }

  // 初始化选项
  private initOptions(): DefaultOptions {
    window.history['pushState'] = createHistoryEvent('pushState')
    window.history['replaceState'] = createHistoryEvent('replaceState')
    return <DefaultOptions>{
      historyTracker: false,
      hashTracker: false,
      domTracker: false,
      jsError: false,
      sdkVersion: TrackerConfig.version
    }
  }

  public setUserId<T extends DefaultOptions['uuid']>(uuid: T) {
    this.data.uuid = uuid
  }

  public setExtra<T extends DefaultOptions['extra']>(extra: T) {
    this.data.extra = extra
  }

  // 暴露给外面使用
  public sendTracker<T>(data: T) {
    this.reportTracker(data)
  }

  // dom上报
  private targetKeyReport() {
    MouseEventList.forEach(evt => {
      window.addEventListener(evt, e=>{
        const target = e.target as HTMLElement
        const targetKey = target.getAttribute('target-key')
        if(targetKey) {
          this.reportTracker({
            event: evt,
            targetKey
          })
        }
      })
    })
  }

  // 监听页面跳转
  private captureEvents <T>(mouseEventList: string[], targetKey: string, data?: T) {
    mouseEventList.forEach(evt => {
      window.addEventListener(evt, () => {
        this.reportTracker({event: evt, targetKey, data})
      })
    })
  }

  // 安装埋点
  private installTracker() {
    if(this.data.historyTracker) {
      this.captureEvents(['pushState', 'replaceState', 'popstate'], 'history-pv')
    }
    if(this.data.hashTracker) {
      this.captureEvents(['hashchange'], 'hash-pv')
    }
    if(this.data.domTracker) {
      this.targetKeyReport()
    }
    if(this.data.jsError) {
      this.jsError()
    }
  }

  // js promise错误
  private jsError() {
    this.errorEvent()
    this.promiseReject()
  }

  private errorEvent() {
    window.addEventListener('error', evt => {
      this.reportTracker({
        event: 'error',
        targetKey: 'message',
        message: evt.message
      })
    })
  }

  private promiseReject() {
    window.addEventListener('unhandledrejection', evt => {
      evt.promise.catch(error => {
        this.reportTracker({
          event: 'promise',
          targetKey: 'message',
          message: error
        })
      })
    })
  }

  // 上报函数
  private reportTracker = <T>(data: T) => {
    const params = Object.assign(this.data, data, {timestamp: Date.now()})
    const headers = {
      type: 'application/x-www-form-urlencoded'
    }
    console.log(JSON.stringify(params))
    const blob = new Blob([JSON.stringify(params)], headers)
    navigator.sendBeacon(this.data.requestUrl, blob)
  }
}